import typing
import ctypes
import psutil
import subprocess
import requests
import time
import socket
import pathlib
import os
from selenium.webdriver.edge.webdriver import WebDriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
import rich


class BrowserController:
    """浏览器控制工具类，负责浏览器进程管理和驱动初始化"""
        
    @staticmethod
    def show_message(parent_hwnd: int = 0, title: str = '提示', text: str = '默认文本', msg_type: int = 0) -> int:
        """显示自定义消息框
        
        Args:
            parent_hwnd: 父窗口句柄
            title: 消息框标题
            text: 消息内容
            msg_type: 消息框类型
            
        Returns:
            int: 用户选择的按钮ID
        """
        return ctypes.windll.user32.MessageBoxW(parent_hwnd, text, title, msg_type)
    
    @staticmethod
    def is_process_running(process_name: str = "msedge.exe") -> bool:
        """检查是否存在指定名称的进程
        
        Args:
            process_name: 进程名称
            
        Returns:
            bool: 进程是否存在
        """
        for proc in psutil.process_iter(['name']):
            if proc.info['name'] == process_name:
                return True
        return False
    
    @staticmethod
    def is_debug_mode_active(local: str = "127.0.0.1", debug_port: str = "9652") -> bool:
        """检查调试模式是否激活
        
        Args:
            local: 本地地址
            port: 端口号
            
        Returns:
            bool: 调试模式是否激活
        """
        url = f"http://{local}:{debug_port}/json/version"
        try:
            response = requests.get(url=url, timeout=5)
            return response.status_code == 200
        except Exception as e:
            rich.print(f"[red]调试模式检查错误: {e}")
            return False
    
    @staticmethod
    def close_all_processes(process_name: str = "msedge.exe") -> bool:
        """关闭所有指定名称的进程
        
        Args:
            process_name: 进程名称
            
        Returns:
            bool: 是否成功关闭所有进程
        """
        success = True
        for proc in psutil.process_iter(['pid', 'name']):
            if process_name == proc.info['name']:
                try:
                    proc.kill()
                    rich.print(f"[green]已关闭进程 PID: {proc.info['pid']}")
                except Exception as e:
                    rich.print(f"[red]关闭进程 PID: {proc.info['pid']} 失败: {e}")
                    success = False
        return success
    
    @staticmethod
    def start_debug_mode_browser(
        local: str = "127.0.0.1",
        debug_port: int = 9652,
        user_data_dir: str = None,
        with_extensions: bool = False
    ) -> None:
        """启动带调试模式的浏览器
        
        Args:
            local: 本地地址
            debug_port: 调试端口
            user_data_dir: 用户数据目录
            with_extensions: 是否启用扩展
        """
        # 构建启动命令
        if user_data_dir:
            command = rf'"C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" --remote-debugging-port={debug_port} --user-data-dir="{pathlib.Path(os.getcwd()).joinpath(user_data_dir).resolve().__str__()}" "http://{local}:{debug_port}/json/version"'
        else:
            command = rf'"C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" --remote-debugging-port={debug_port} "http://{local}:{debug_port}/json/version"'
        if not with_extensions:
            command = command + " --disable-extensions"
        
        # 启动新进程
        CREATE_NEW_CONSOLE = 0x00000010
        subprocess.Popen(command, shell=True, creationflags=CREATE_NEW_CONSOLE)
        rich.print(f"[green]已启动调试模式浏览器，端口: {debug_port}")
        time.sleep(3)  # 等待浏览器启动
                
    @staticmethod
    def is_port_used(
            port: int,
            host: str = "127.0.0.1",
            proto: str = "tcp"
    ) -> bool:
        """检查指定主机的端口是否被占用。

        Args:
            port: 待检测的端口号，有效范围为 0-65535。
            host: 主机地址，默认为本地回环地址 "127.0.0.1"。
            proto: 协议类型，支持 "tcp" 或 "udp"，默认 "tcp"。

        Returns:
            如果端口被占用，返回 True；否则返回 False。

        Raises:
            ValueError: 当端口号超出有效范围（0-65535）时触发。
            ValueError: 当协议类型不是 "tcp" 或 "udp" 时触发。
        """
        # 验证端口有效性
        if not (0 <= port <= 65535):
            raise ValueError(f"无效端口：{port}（有效范围：0-65535）")
        
        # 验证协议类型
        proto_lower = proto.lower()
        if proto_lower not in ("tcp", "udp"):
            raise ValueError(f"不支持的协议：{proto}（仅支持 tcp/udp）")

        # 确定套接字类型
        sock_type = socket.SOCK_STREAM if proto_lower == "tcp" else socket.SOCK_DGRAM

        # 创建并使用套接字尝试绑定
        with socket.socket(socket.AF_INET, sock_type) as sock:
            try:
                if proto_lower == "tcp":
                    # TCP 协议需要绑定并监听
                    sock.bind((host, port))
                    sock.listen(1)
                else:
                    # UDP 协议仅需绑定
                    sock.bind((host, port))
                return False  # 绑定成功，端口未被占用
            except OSError:
                # 捕获地址占用相关的操作系统错误
                return True  # 绑定失败，端口已被占用

    @staticmethod
    def force_start_debug_browser(
        local: str = "127.0.0.1",
        debug_port: int = 9652,
        user_data_dir: typing.Optional[str] = None
    ) -> typing.Tuple[WebDriver, WebDriverWait, ActionChains]:
        """强制启动浏览器

        如果检测到当前进程中不存在调试模式的浏览器进程，直接强制启动新的调试模式浏览器

        Args:
            _local: 本地地址
            _debug_port: 调试端口
            _user_data_dir: 用户数据目录
        """
        if not BrowserController.is_debug_mode_active(local=local, debug_port=debug_port):
            BrowserController.close_all_processes(process_name="msedge.exe")
            BrowserController.start_debug_mode_browser(local=local, debug_port=debug_port,user_data_dir=user_data_dir)
    
    @staticmethod
    def safe_start_debug_browser(
        local: str = "127.0.0.1",
        debug_port: int = 9652,
        user_data_dir: typing.Optional[str] = None
    ) -> typing.Tuple[WebDriver, WebDriverWait, ActionChains]:
        
        if BrowserController.is_debug_mode_active(local=local, debug_port=debug_port):
            # 检测到当前进程中存在调试模式浏览器进程,因此不用进行额外的启动
            print(f"提示 : 剪刀到当前进程为调试模式!")
            return 
        else:
            # 检测到当前进程中不存在调试模式浏览器进程,因此需要启动调试模式浏览器
            
            if BrowserController.is_port_used(port=debug_port,host=local):
                rich.print(f"[yellow]端口 {debug_port} 已被占用，请检查端口是否被其他程序占用")
                rich.print("[red]端口被占用，请检查端口是否被其他程序占用")
                exit(0)
                
            else:
                if user_data_dir:
                    rich.print(f"[green]已指定用户数据目录：{user_data_dir}")
                    rich.print(pathlib.Path(os.getcwd()).joinpath(user_data_dir).resolve())
                    rich.print(f"[yellow]已检测到当前进程中不存在调试模式的浏览器进程，将尝试启动调试模式浏览器")
                    BrowserController.start_debug_mode_browser(local=local, debug_port=debug_port,user_data_dir=user_data_dir,with_extensions=False)
                else:
                    BrowserController.close_all_processes(process_name="msedge.exe")
                    BrowserController.start_debug_mode_browser(local=local, debug_port=debug_port,with_extensions=False)